package com.simp.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.simp.dto.GroovyDto;
import com.simp.entity.SysCategoryEntity;
import com.simp.entity.SysScriptEntity;
import com.simp.expection.GlobalException;
import com.simp.mapper.SysScriptMapper;
import com.simp.page.PageVO;
import com.simp.page.R;
import com.simp.utils.ExceptionUtils;
import com.simp.utils.GroovyUtils;
import com.simp.utils.JwtUtil;
import com.simp.vo.GroovyVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author pan.shun
 * @since 2022/3/22 15:53
 */
@Service
@Slf4j
public class GroovyService extends ServiceImpl<SysScriptMapper, SysScriptEntity> {

    @Autowired
    private SysCategoryService sysCategoryService;

    public R getPageList(GroovyDto groovyDto) {
        LambdaQueryWrapper<SysScriptEntity> queryWrapper = new LambdaQueryWrapper<>();
        if (!StringUtils.isEmpty(groovyDto.getName())) {
            queryWrapper.like(SysScriptEntity::getName, groovyDto.getName());
        }
        if (!StringUtils.isEmpty(groovyDto.getType())) {
            queryWrapper.eq(SysScriptEntity::getType, groovyDto.getType());
        }
        if (!StringUtils.isEmpty(groovyDto.getTypeId())) {
            Set<String> idList = new HashSet<>(16);
            idList.add(groovyDto.getTypeId());
            findAllChildren(idList, groovyDto.getTypeId());
            queryWrapper.in(SysScriptEntity::getTypeId, idList);
        }

        queryWrapper.orderByDesc(SysScriptEntity::getCreateTime);
        Page<SysScriptEntity> page = this.page(
                new Page<>(groovyDto.getCurrent(), groovyDto.getSize()), queryWrapper
        );
        return R.success(new PageVO<>(page));
    }

    private void findAllChildren(Set<String> idList, String id) {
        LambdaQueryWrapper<SysCategoryEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(SysCategoryEntity::getId);
        queryWrapper.eq(SysCategoryEntity::getParentId, id);
        List<SysCategoryEntity> childrenList = sysCategoryService.list(queryWrapper);
        if (!CollectionUtils.isEmpty(childrenList)) {
            childrenList.forEach(r -> {
                idList.add(r.getId());
                findAllChildren(idList, r.getId());
            });
        }
    }

    public R addOrUpdateScript(SysScriptEntity sysScriptEntity, HttpServletRequest request) {
        checkRepeated(sysScriptEntity);
        if (StringUtils.isEmpty(sysScriptEntity.getId())) {
            sysScriptEntity.setCreateBy(JwtUtil.getJwtClaimsDetailByRequest(request).getUserName());
            this.save(sysScriptEntity);
        } else {
            sysScriptEntity.setUpdateBy(JwtUtil.getJwtClaimsDetailByRequest(request).getUserName());
            this.updateById(sysScriptEntity);
        }
        return R.success();
    }

    private void checkRepeated(SysScriptEntity sysScriptEntity) {
        LambdaQueryWrapper<SysScriptEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysScriptEntity::getName, sysScriptEntity.getName());
        if (StringUtils.isEmpty(sysScriptEntity.getId())) {
            int count = this.count(queryWrapper);
            if (count != 0) {
                throw new GlobalException("脚本名称不能重复");
            }
        } else {
            queryWrapper.ne(SysScriptEntity::getId, sysScriptEntity.getId());
            int count = this.count(queryWrapper);
            if (count != 0) {
                throw new GlobalException("脚本名称不能重复");
            }
        }
    }

    public R removeScriptById(String id) {
        this.removeById(id);
        return R.success();
    }

    public R runFileScript(MultipartFile file, String id) {
        SysScriptEntity script = this.getById(id);
        return runGroovy(script, file);
    }


    public R runScript(GroovyVo groovyVo) {
        SysScriptEntity script = this.getById(groovyVo.getId());
        if (!groovyVo.getParamValue().isEmpty()) {
            JSONObject paramValue = groovyVo.getParamValue();
            List<Object> objectList = new ArrayList<>();
            List<String> paramsNameString = GroovyUtils.getParamsNameString(script.getGroovyScript(), script.getInvokeFunction());
            paramsNameString.forEach(r -> {
                objectList.add(paramValue.get(r));
            });
            if (objectList.size() == 1) {
                return runGroovy(script, objectList.get(0));
            } else {
                return runGroovy(script, objectList);
            }
        } else {
            return runGroovy(script, null);
        }
    }

    /**
     * 脚本执行
     *
     * @param script 脚本内容
     * @param val    参数值
     */
    private R runGroovy(SysScriptEntity script, Object... val) {
        try {
            Object invoke = GroovyUtils.invoke(script.getGroovyScript(), script.getInvokeFunction(), val);
            script.setErrorMsg("");
            this.updateById(script);
            return R.success(invoke);
        } catch (Exception e) {
            String print = ExceptionUtils.print(e);
            script.setErrorMsg(print);
            this.updateById(script);
            return R.error("脚本执行异常");
        }
    }

}
